package com.kinyx.hibernate.usertype;

import java.io.Serializable;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;
import java.util.BitSet;
import java.util.Objects;

import org.hibernate.HibernateException;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.type.StringType;
import org.hibernate.usertype.UserType;

import com.kinyx.hibernate.type.descriptor.java.BitSetBasicTypeDescriptor;

public class BitSetUserType implements UserType {

	public static final BitSetUserType INSTANCE = new BitSetUserType();

	@Override
	public int[] sqlTypes() {
		return new int[]{
				StringType.INSTANCE.sqlType()
		};
	}

	@Override
	public Class<?> returnedClass() {
		return BitSet.class;
	}

	@Override
	public boolean equals(final Object x, final Object y) throws HibernateException {
		return Objects.equals(x, y);
	}

	@Override
	public int hashCode(final Object x) throws HibernateException {
		return Objects.hashCode(x);
	}

	@Override
	public Object nullSafeGet(final ResultSet rs, final String[] names, final SharedSessionContractImplementor session, final Object owner) throws HibernateException, SQLException {
		final String columnName = names[0];
		final String columnValue = (String) rs.getObject(columnName);
		return columnValue == null ? null : BitSetBasicTypeDescriptor.INSTANCE.fromString(columnValue);
	}

	@Override
	public void nullSafeSet(final PreparedStatement st, final Object value, final int index, final SharedSessionContractImplementor session) throws HibernateException, SQLException {
		if (value == null) {
			st.setNull(index, Types.VARCHAR);
		} else {
			final String stringValue = BitSetBasicTypeDescriptor.INSTANCE.toString((BitSet) value);
			st.setString(index, stringValue);
		}
	}

	@Override
	public Object deepCopy(final Object value) throws HibernateException {
		return value == null ? null : BitSet.valueOf(BitSet.class.cast(value).toLongArray());
	}

	@Override
	public boolean isMutable() {
		return true;
	}

	@Override
	public Serializable disassemble(final Object value) throws HibernateException {
		return (BitSet) this.deepCopy(value);
	}

	@Override
	public Object assemble(final Serializable cached, final Object owner) throws HibernateException {
		return this.deepCopy(cached);
	}

	@Override
	public Object replace(final Object original, final Object target, final Object owner) throws HibernateException {
		return this.deepCopy(original);
	}
}
